using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FernUniHagen.CompilerExtension.DataStructures;
using FernUniHagen.CompilerExtension.Functions;
using FernUniHagen.CompilerExtension.Interfaces;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;

namespace FernUniHagen.CompilerExtension.Adaptor
{
    public class QueryExpressionAdaptor : BlockNodeFactory, IMultiplicityConvertible<CommonSyntaxNode>, IReferencable
    {
        #region Fields
        QueryExpressionSyntax _queryExpresion;
        IDocument _doc;
        BlockSyntax _block;
        StatementSyntax _stmt;
        IdentifierNameSyntax _identifier;
        #endregion

        #region Constructor
        public QueryExpressionAdaptor(QueryExpressionSyntax node)
        {
            _queryExpresion = node;
        }

        public QueryExpressionAdaptor(IDocument doc, BlockSyntax block, QueryExpressionSyntax queryExpression, StatementSyntax stmt, IdentifierNameSyntax identifer, ISemanticModel model) : this(queryExpression)
        {
            _doc = doc;
            _block = block;
            _stmt = stmt;
            _identifier = identifer;
            SemanticModel = model;
        }
        #endregion

        public override StatementReplacement<CommonSyntaxNode> ConvertToMutiplicity(string newVariableName)
        {
            var identifierName = (IdentifierNameSyntax)_queryExpresion.DescendantNodes().First(p => p is IdentifierNameSyntax);

            if (identifierName == null) throw new Exception("Could not find Identifier in Linq Statement");

            CommonSyntaxNode decleration = SemanticModel.FindDecleration(identifierName);
            TypeSyntax typeOfDeclaration = null;
            if (decleration is FieldDeclarationSyntax)
                typeOfDeclaration = (TypeSyntax)((FieldDeclarationSyntax)decleration).Declaration.DescendantNodes().First(p => p is TypeSyntax);

            if (decleration is PropertyDeclarationSyntax)
                typeOfDeclaration = ((PropertyDeclarationSyntax)decleration).Type;
            //TODO: Also Properties needs to be added see empty if

            string linqVariable = VariableCreatorService.GetTempVariable();

            var dummyList = Syntax.ParseStatement(string.Format("System.Collections.Generic.List<{0}> {1} = new System.Collections.Generic.List<{0}>() {{ {2} }};" +
                Environment.NewLine,
                typeOfDeclaration.ToString(), linqVariable, newVariableName.ToString()));

            var newIdentifier = Syntax.IdentifierName(linqVariable);

            StatementReplacement<CommonSyntaxNode> replacement = 
                new StatementReplacement<CommonSyntaxNode>(new List<StatementSyntax>() { dummyList }, identifierName, newIdentifier);
            
            return replacement;
        }

        public SyntaxNodeInfo GetReference()
        {
            //first get the identifier and its declaration (a field or a property)
            var identifierName = (IdentifierNameSyntax)_queryExpresion.DescendantNodes().First(p => p is IdentifierNameSyntax);
            if (identifierName == null) throw new Exception("Could not find Identifier in Linq Statement");
            CommonSyntaxNode decleration = SemanticModel.FindDecleration(identifierName);

            //Now get the type of this declaration
            TypeSyntax typeOfDeclaration = null;
            if (decleration is FieldDeclarationSyntax)
                typeOfDeclaration = (TypeSyntax)((FieldDeclarationSyntax)decleration).Declaration.DescendantNodes().First(p => p is TypeSyntax);
            else if (decleration is PropertyDeclarationSyntax)
                typeOfDeclaration = ((PropertyDeclarationSyntax)decleration).Type;
            else throw new Exception("The Identifier in the Linq statement is currently not supported");

            //And finally get the declaration of this type which will be the class which we want to change
            var typeIdentifierName = (IdentifierNameSyntax)typeOfDeclaration.DescendantNodesAndSelf().First(n => n is IdentifierNameSyntax);
            SyntaxNode typeClass = (SyntaxNode)SemanticModel.FindDecleration(typeIdentifierName);

            //  SyntaxNodeInfo nodeInfo = new SyntaxNodeInfo(doc, typeClass, AttributeType.Any, queryExpression, stmt, null);
            SyntaxNodeInfo nodeInfo = new SyntaxNodeInfo(_doc, _block, _queryExpresion, _stmt, _identifier);
            return nodeInfo;

        }
    }
}
